﻿using System;
using System.Collections.Generic;
using Encog.Neural.Networks;
using MentalAlchemy.Atomics;

namespace MentalAlchemy.Molecules.MachineLearning
{
	public class TimeSeriesElements
	{
		#region - Analysis. -
		/// <summary>
		/// [molecule]
		/// 
		/// Select window size for a given time-series using the 1st zero of the autocorrelation function.
		/// This criterion is (theoretically) more suitable for chaotic time-series.
		/// </summary>
		/// <param name="data">Time-series.</param>
		/// <param name="maxTau">Maximal possible value for a window size.</param>
		/// <returns></returns>
		public static int AutocorrelationDelay(float[] data, int maxTau)
		{
			//const float eps = 0.05f;

			var size = data.Length;
			var autoCor = new float[maxTau - 1];
			for (int i = 1; i < maxTau; i++)
			{
				var temp = 0f;
				for (int k = 0; (k + i) < size; k++)
				{
					temp += data[k] * data[k + i];
				}
				autoCor[i - 1] = Math.Abs(temp) / (size - i);
				//if (autoCor[i - 1] < eps) { return i; }
			}

			return VectorMath.IndexOfMin(autoCor);
		} 
		#endregion

		#region - Conversion. -
		/// <summary>
		/// [molecule]
		/// 
		/// Returns deltas for the given time-series.
		/// </summary>
		/// <param name="data"></param>
		/// <returns></returns>
		public static float[] GetDeltas (float[] data)
		{
			var size = data.Length - 1;
			var res = new float[size];
			for (int i = 0; i < size; i++)
			{
				res[i] = data[i + 1] - data[i];
			}
			return res;
		}

		/// <summary>
		/// [molecule]
		/// 
		/// Creates training data samples for prediction on [predWindow] values
		/// for prescribed data and parameters.
		/// </summary>
		/// <param name="data">Time-series.</param>
		/// <param name="sampleSize">Number of data points in each training sample.</param>
		/// <param name="predWindow">Number of predicted values.</param>
		/// <returns>List of training samples.</returns>
		public static List<TrainingSample> CreatePredictionSamples (float[] data, int sampleSize, int predWindow)
		{
			var inputs = sampleSize;
			var outputs = predWindow;
			var tData = MachineLearningElements.CreateFromTimeSeries(data, inputs);

			// fill-in the responses.
			for (int i = 0; i < tData.Count - outputs; i++)
			{
				var temp = new float[1, outputs];
				for (int j = 0; j < outputs; j++)
				{
					temp[0, j] = data[inputs + i + j];
				}
				tData[i].Response = temp;
				//tData[i].Response = new[,] { { data[inputs + i] } };
			}

			// remove the last [outputs] elements.
			for (int i = 0; i < outputs; i++)
			{
				tData.RemoveRange(tData.Count - outputs, outputs);
			}

			return tData;
		}
		#endregion

		#region - Algorithms' training. -
		/// <summary>
		/// [molecule]
		/// 
		/// Trains Encog ANN to predict next value in the time-series.
		/// </summary>
		/// <param name="props"></param>
		/// <param name="tData"></param>
		/// <param name="epochs"></param>
		/// <param name="ers"></param>
		/// <returns></returns>
		public static BasicNetwork TrainPredictEncog(NeuralNetProperties props, List<TrainingSample> tData, int epochs, out List<float> ers)
		{
			var net = MachineLearningElements.CreateEncogBasicNetwork(props);
			var data = MachineLearningElements.CreateEncogTrainingData(tData);

			ers = MachineLearningElements.TrainEncogNetwork(net, data, epochs);
			return net;
		}
		#endregion
	}
}